<html>
    <head>
        <title>领域服务</title>
    </head>
    <body>
        <script>
            // 领域服务

                    /*
                        在域驱动设计（DDD）解决方案中，核心业务逻辑通常在"聚合（实体)" 和 "领域服务"中实现.

                        因此，在以下情况下，需要你创建领域服务：

                                    你想实现依赖于某些服务（如存储库或其他外部服务）的核心域逻辑。（领域服务可以访问存储库，领域服务中可以注入应用程序服务）

                                    你需要实现的逻辑与多个聚合/实体相关，因此它不适合任何聚合。（要实现的核心逻辑不适合与放在任何实体和聚合根中）
                    */

                    /*
                        一、ABP 领域服务基础设置

                                  域服务是简单的无状态类。虽然你不必从任何服务或接口派生，但ABP提供了一些有用的基类和约定


                                  1、DomainService基类  和 IDomainService接口

                                                        eg：  创建从DomainSercvice 基类派生的领域服务：

                                                                using Volo.Abp.Domain.Services;

                                                                namespace MyProject.Issues
                                                                {
                                                                    // 领域服务类名以"Manager"为后缀
                                                                    public class IssueManager : DomainService
                                                                    {
                                                                        
                                                                    }
                                                                }


                                                        当你这样做的时候：

                                                            ABP自动将类注册到具有瞬态生命周期的依赖注入系统。

                                                            你可以直接使用一些常用服务作为基本属性，而无需手动注入（例如ILogger和IGuidGenerator）。



                                                        eg: 实现将问题分配给用户的域逻辑

                                                                public class IssueManager : DomainService
                                                                {
                                                                    private readonly IRepository<Issue, Guid> _issueRepository;

                                                                    public IssueManager(IRepository<Issue, Guid> issueRepository)
                                                                    {
                                                                        _issueRepository = issueRepository;  // 领域服务中可以使用存储库
                                                                    }
                                                                    
                                                                    public async Task AssignAsync(Issue issue, AppUser user)
                                                                    {
                                                                        var currentIssueCount = await _issueRepository
                                                                            .CountAsync(i => i.AssignedUserId == user.Id);
                                                                        
                                                                        // 实现业务验证
                                                                        if (currentIssueCount >= 3)
                                                                        {
                                                                            throw new IssueAssignmentException(user.UserName);
                                                                        }

                                                                        issue.AssignedUserId = user.Id;
                                                                    }    
                                                                }


                                                        Issue 是一个聚合根，定义如下：

                                                                public class Issue : AggregateRoot<Guid>
                                                                {
                                                                    public Guid? AssignedUserId { get; internal set; }
                                                                    
                                                                    //...
                                                                }

                                                        将setterinternal可确保它不能直接在上层设置，并强制始终使用IssueManager将Issue分配给User。



                                  2、如何使用领域服务：

                                            域服务通常用于应用程序服务。

                                            eg:  使用IssueManager将问题分配给用户


                                                        using System;
                                                        using System.Threading.Tasks;
                                                        using MyProject.Users;
                                                        using Volo.Abp.Application.Services;
                                                        using Volo.Abp.Domain.Repositories;

                                                        namespace MyProject.Issues
                                                        {
                                                            public class IssueAppService : ApplicationService, IIssueAppService
                                                            {
                                                                private readonly IssueManager _issueManager;
                                                                private readonly IRepository<AppUser, Guid> _userRepository;
                                                                private readonly IRepository<Issue, Guid> _issueRepository;

                                                                public IssueAppService(
                                                                    IssueManager issueManager,
                                                                    IRepository<AppUser, Guid> userRepository,
                                                                    IRepository<Issue, Guid> issueRepository)
                                                                {
                                                                    _issueManager = issueManager;
                                                                    _userRepository = userRepository;
                                                                    _issueRepository = issueRepository;
                                                                }

                                                                public async Task AssignAsync(Guid id, Guid userId)
                                                                {
                                                                    var issue = await _issueRepository.GetAsync(id);
                                                                    var user = await _userRepository.GetAsync(userId);

                                                                    await _issueManager.AssignAsync(issue, user);
                                                                    await _issueRepository.UpdateAsync(issue);
                                                                }
                                                            }
                                                        }

                                         由于IssueAppService位于应用层，因此它不能直接将问题分配给用户。因此，它使用IssueManager。
                                        IssueManager位于域层，可以访问存储库。  

                    */


                    /*
                        二、领域服务与应用服务

                                    虽然Application Services和Domain Services都实现了业务规则，但存在根本的逻辑和形式差异：

                                        ①、Application Services实现应用程序的用例（典型Web应用程序中的用户交互），而Domain Services实现核心、与用例无关的域逻辑

                                        ②、Application Services获取/返回数据传输对象，域服务方法通常获取并返回领域对象（实体，值对象）。

                                        ③、域服务通常由应用程序服务或其他域服务使用，而应用程序服务由表示层或客户端应用程序使用。
                    */

                    /*
                        三、生命周期 

                                    域服务的生命周期是瞬态，它们会自动注册到依赖注入系统。
                    */
        </script>
    </body>
</html>